package com.twitterbuzz.server;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

import twitter4j.ProfileImage;
import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.Status;
import twitter4j.Trend;
import twitter4j.Trends;
import twitter4j.IDs;

import twitter4j.Tweet;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.AccessToken;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.twitterbuzz.client.TwitterBuzzService;
import com.twitterbuzz.shared.Result;
import com.twitterbuzz.shared.TweetInfo;
import com.twitterbuzz.shared.TweetInfo.sentimentType;
import com.twitterbuzz.shared.UserInfoClient;

@SuppressWarnings("serial")
public class TwitterBuzzServiceImpl extends RemoteServiceServlet implements TwitterBuzzService {	
	private Twitter twitter;				
		
	@Override
	public void init() {
		twitter = new TwitterFactory().getInstance();
		twitter.setOAuthConsumer("CrxnozhcSoSwLIiXKhwE0g", "2ZLI2HnRW6aZNEIbXK1SUzKGsKBGdFZCNJPf6tDhXY");
    	AccessToken accessToken = new AccessToken("53318115-51lCh0EjEgJbUD0bP1XcgW7CXArRcHVjYQ1ePVDOC","QCXnzBWWGAiElALtqMnA2nlut6u0cIEKRS9nMVAlUA");
    	twitter.setOAuthAccessToken(accessToken);		
	}
	
	@Override
	public UserInfoClient authenticateUser(String userName, String passwd) {
		UserInfoClient output = null;
		User user = null;
    	try {
    		user = twitter.showUser(userName);
    		ProfileImage image = null;
			try {
				image = twitter.getProfileImage(user.getScreenName(), ProfileImage.NORMAL);
			} catch (TwitterException e) {
				e.printStackTrace();
			}
    		output = new UserInfoClient(user.getName(), user.getScreenName(), 
    				user.getLocation(), image.getURL(), user.getDescription());
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		return output;
	}
	
	@Override
	public Result[] getResults(String userName) {
		Result[] results = null;
		List<TrendBucket> trendBuckets = new ArrayList<TrendBucket>();
		// get trends		
	    try{
		    List<Trends> trendsList = twitter.getDailyTrends();		    
		    int k = 0;
		    for(Trends trends: trendsList) {		    	 
		    	for (Trend trend : trends.getTrends()) {
			    	if(trend.getName()!= null){			    		
			    		Query query = new Query(trend.getName());
			    		System.out.print("The trend name is " + trend.getName());
			    	    QueryResult result = twitter.search(query);
			    	    String tweets = "";
			    	    for (Tweet tweet : result.getTweets()) {
			    	    	tweets += " " + tweet.getText();			    	        
			    	    }
			    	    HashMap<String, Double> tfv = parseText(tweets);
			    	    HashMap<TweetInfo, Double> ti = new HashMap<TweetInfo, Double>();
			    	    TrendBucket obj = new TrendBucket(trend.getName(), tweets, tfv, ti);
			    	    trendBuckets.add(obj);			    	    
			    	}
		            k++;
		           
		        }
		    	break;
		    }		    
		    System.out.print("The value of k" + k);
		    results = new Result[k];
		    		    
		    /*IDs ids;
		    int maxFriends = 10;
		    User usr = twitter.showUser(userName);
	        ids = twitter.getFriendsIDs(usr.getScreenName(), 0);
	        for (long id : ids.getIDs()) {
	        	if (maxFriends-- == 0) break;
	        	List<Status> statuses;
	        	statuses = twitter.getUserTimeline(id);	        		
	        	for (Status status : statuses) {
	        		HashMap<String, Double> tfv = parseText(status.getText());
	        		double maxSim = 0;
	        		int maxTopicIndex = -1;
	        		int index = 0;
	        		for (TrendBucket trendBucket : trendBuckets) {
	        			double sim = calculateSimilarity(tfv, trendBucket.normTFV);
	        			if (sim > maxSim) {
	        				maxSim = sim;
	        				maxTopicIndex = index;
	        			}
	        			index++;
	        		}
	        		User user = status.getUser();
	        		ProfileImage image = null;
	        		try {
	        			image = twitter.getProfileImage(user.getScreenName(), ProfileImage.NORMAL);
	        		} catch (TwitterException e) {
	        			e.printStackTrace();
	        		}
	           		UserInfoClient userInfo = new UserInfoClient(user.getName(), user.getScreenName(), 
	           								user.getLocation(), image.getURL(), user.getDescription());
	           		
	        		TweetInfo ti = new TweetInfo(status.getText(), userInfo, sentimentType.GOOD);
	        		trendBuckets.get(maxTopicIndex).tweetInfos.put(ti, maxSim);
	        	}
	        }       
		    IDs ids = null;
		    try{
		    	
		    	long cursor = -1;
		    	System.out.println("Listing followers's ids.");
		        do {
		        	 ids = twitter.getFollowersIDs(userName,cursor);//Here I am giving my screen name	 
		        } while ((cursor = ids.getNextCursor()) != 0);
		        
		    }
		    catch (TwitterException te) {
	            te.printStackTrace();
	            System.out.println("Failed to get followers' ids: " + te.getMessage());
	            System.exit(-1);
	        }
		    
		    
		    
		  //  List<Status> statuses;
		    //long id = ids.getIDs()[0];
    		// followers tweerts
		//    statuses = twitter.getPublicTimeline();
		    //statuses = twitter.getUserTimeline(id);
		    */
		    /*
		   List<List<Status>> statusList = new ArrayList<List<Status>>();
		   Boolean flag = true;
		   int i = 0;
		   while(flag){	
			   try{			
				   for(; i < ids.getIDs().length;){
		    			List<Status> status = twitter.getUserTimeline(ids.getIDs()[i]);
					//	List<Status> status = twitter.getPublicTimeline();
		    			i++;
					
		    			statusList.add(status);
		    			/*if(i == 1){
		    				flag = false;
                           	break;
                         }
		   	   	 	}
			    	if(i == ids.getIDs().length){
					flag = false;
			    	}
			    
			   }
			   catch(TwitterException te){
	   	 			i++; 
	   	 		}
		}  */
		 // for(List<Status>  statuses: statusList){
			  User user = null;
			  ProfileImage image = null;
			  UserInfoClient userInfo = null;
			  Boolean flag1 = true;
			  String screenName = null;
  			  String name = null;
  			  String location = null;
  			  String description = null;
  			  String imgUrl = null;
  			  int x=5;
  			  List<Status> statuses = twitter.getFriendsTimeline();
	    		for (Status status : statuses) {
	    			String statusText = status.getText();
	    			System.out.println(statusText);
	    			System.out.println(getSentimentType(status.getText()));
	    			HashMap<String, Double> tfv = parseText(statusText);
	    			double maxSim = 0;
	    			int maxTopicIndex = -1;
	    			int index = 0;
	    			for (TrendBucket trendBucket : trendBuckets) {
	    				//double sim = calculateSimilarity(tfv, trendBucket.normTFV);
	    				 
	    				if(trendBucket.trend.startsWith("#")) {
	    	    			trendBucket.trend = trendBucket.trend.substring(1);
	    	            }
	    				String trends[] = trendBucket.trend.split(" ");
	    				double sim = findSimilarity(tfv,trends,trendBucket.normTFV);
	    				if (sim > maxSim) {
	    					maxSim = sim;
	    					maxTopicIndex = index;
	    				}
	    				index++;
	    			}
	    			if (maxTopicIndex == -1) continue;
	    			//if(flag1){
		    			
		    			try {
		    				user = status.getUser();
			    			screenName = user.getScreenName();
			    			name = user.getName();
			    			location = user.getLocation();
			    			description = user.getDescription();
		    				image = twitter.getProfileImage(user.getScreenName(), ProfileImage.NORMAL);
		    				imgUrl = image.getURL();
		    			} catch (TwitterException e) {
		    				e.printStackTrace();
		    				continue;
		    			}
		    					    				    			
		    			//flag1 = false;
	    			//}
		    			userInfo = new UserInfoClient(name, screenName, 
								location, imgUrl, description);
		    			TweetInfo ti = new TweetInfo(status.getText(), userInfo,getSentimentType(status.getText()));
		    			trendBuckets.get(maxTopicIndex).tweetInfos.put(ti, maxSim);
	    			/*
	    			if(user !=null && image != null){
		    			if(name != null && screenName != null  && location != null && imgUrl != null && description != null){
		    				
		    			}
	    			}
	    			
	        		// write some function and make a call to it, getsentimenttype(string tweet) return sentiment type
	        		// built an decent list of +ve -ve word list
	        		
	        		//sentimentType st = getSentimentType(status.getText());
	    			if(userInfo != null && statusText != null){
		    		//	TweetInfo ti = new TweetInfo(statusText, userInfo, sentimentType.GOOD);
					
	    			}*/
	    			x--;
	    			
	       		}
		   // }
    		int index = 0;
    		for (TrendBucket trendBucket : trendBuckets) {
    			results[index] = new Result();
    			results[index].trend = trendBucket.trend;
    			results[index].trendingTweets = trendBucket.trendingTweets;    			
    			//ArrayList aList = new ArrayList(trendBucket.tweetInfos.entrySet());
    			Map<TweetInfo,Double> aList = new HashMap<TweetInfo,Double>();
    			aList.putAll(trendBucket.tweetInfos);
    			ValueComparator bvc =  new ValueComparator(aList);
    	        TreeMap<TweetInfo,Double> sorted_map = new TreeMap<TweetInfo,Double>(bvc);
    	       

    	        sorted_map.putAll(aList);


    			//ArrayList<TweetInfo,Double> aList = new ArrayList<TweetInfo,Double>();
    			//Set<TweetInfo> s = trendBucket.tweetInfos.entrySet();
    			
    			//Collections.sort(aList, new doubleComparator());				
    			//Iterator itr = aList.iterator();
    			// subject to change
    			//int max = 2;
    			int j = 0;
    			results[index].tweetInfos = new TweetInfo[k];
    			Set<TweetInfo> s = sorted_map.keySet();
    			Iterator<TweetInfo> itr = s.iterator();
    			/* for (TweetInfo key : sorted_map.keySet()) {
    				 results[index].tweetInfos[j++] = key;
    				 max--;
    				 if(max < 0){ break;}
    		        }
*/
    		
    			//while(itr.hasNext() && (max--) != 0) {
    			while(itr.hasNext()) {
    				//Map.Entry m=(Map.Entry)itr.next();
    				TweetInfo ti = itr.next();    				
    				results[index].tweetInfos[j++] = ti;
    			}
    			index++;
    		}
	        
	    } catch(TwitterException e){
	    	e.printStackTrace();
	    }
	    
	    return results;
	}
	
	/*public void addToHash(Status tweet) {
		Pattern p = Pattern.compile("[\\s;,\\.]+");
    	String tokens[] = p.split(tweet.getText());
    	for(String token: tokens){
    		if(token.startsWith("#")) {
    			token = token.substring(1);
            }
    		if (tfHash.containsKey(token)) {
                tfHash.put(token, tfHash.get(token) + 1.0);
            }
            else{
                tfHash.put(token, 1.0);
    		}
    	}
	}*/
	
	public HashMap<String, Double> parseText(String text) {
		HashMap<String, Double> tfHash = new HashMap<String, Double>();
		Pattern p = Pattern.compile("[\\s;,\\.]+");
    	String tokens[] = p.split(text);
    	tfHash.clear();
    	
    	for(String token: tokens){
    		if(token.startsWith("#")) {
    			token = token.substring(1);
            }
    		if (tfHash.containsKey(token)) {
                tfHash.put(token, tfHash.get(token) + 1.0);
            }
            else{
                tfHash.put(token, 1.0);
    		}
    	}
    	
    	Set<String> words = null;
        words = tfHash.keySet();
        
        // Calculating 1+log TERM FREQUENCY	        
        for(Iterator<String> it = words.iterator();it.hasNext(); ){
	         String key = it.next();
	         tfHash.put(key,(1+Math.log(tfHash.get(key))));
        }
        
        //Normalization(Dividing value by square root of sum of squares of all values	        
        double sum = 0.0, val = 0.0;       
        words = tfHash.keySet();
        for(Iterator<String> it = words.iterator();it.hasNext();){
        	val = tfHash.get(it.next());
            sum += (val*val);
        }
        sum = Math.sqrt(sum);             
        words = tfHash.keySet();
        
        for(Iterator<String> it = words.iterator();it.hasNext(); ) {
        	String key = it.next();
        	val = tfHash.get(key);
        	tfHash.put(key,(val/sum));
        }
        return tfHash;
	}
	
	double calculateSimilarity(HashMap<String, Double> tfv1, HashMap<String, Double> tfv2) {
		double sim = 0;
		Set<String> words = tfv1.keySet();
		for(Iterator<String> it = words.iterator();it.hasNext(); ) {
			String key = it.next();
			double val = tfv1.get(key);
			if (tfv2.containsKey(key)) {
				sim += (val*tfv2.get(key));
			}
		}
		return sim;
	}
	
	double findSimilarity(HashMap<String, Double> tfv1, String trends[],HashMap<String, Double> tfv2) {
		double sim = 0;
		Boolean flag = false;
		Set<String> words = tfv1.keySet();
		for(Iterator<String> it = words.iterator();it.hasNext(); ) {
			String key = it.next();
			for(String trend: trends){
				if(key.equals(trend)){
					flag = true;
					System.out.println("Reached flag case" + trend + "sim " + sim);
				}
			}
			double val = tfv1.get(key);
			if (tfv2.containsKey(key)) {
				sim += (val*tfv2.get(key));
			}
		}
		if(flag == true) { 
			return sim;
		}
		else {
			return 0;
			
		}
	}
	
	public sentimentType getSentimentType(String tweetcontent){
		//sentimentType st = null;
		List<String> positive_words = new ArrayList<String>();
		    positive_words.add(":)");
		    positive_words.add("happy");
		    positive_words.add("good");
		    positive_words.add("excellent");
		List<String> negative_words = new ArrayList<String>();
		    negative_words.add(":(");
		    negative_words.add("sad");
		    negative_words.add("bad");
		    negative_words.add("worst");
		List<String> neutral_words = new ArrayList<String>();
		    neutral_words.add("ok");
		    negative_words.add("decent");
	    
		String strline = tweetcontent;
		 //Fill HashMap
		        
        	HashMap<String, Double> tweet = new HashMap<String, Double>();
        	Pattern p = Pattern.compile("[\\s;,\\.]+");
        	String tokens[]=p.split(strline);
        	for(String token: tokens){
        		if(token.startsWith("#")) {
        			token=token.substring(1);
                	}
        		if (tweet.containsKey(token)) {
	                    tweet.put(token, tweet.get(token) + 1.0);
        	        }
	                else{
	                    tweet.put(token, 1.0);
        		}
        	}
        	int positive = 0;int negative = 0;int neutral = 0;
        	//We can actually check if the tweet contains Trending Topic
            	for(String pos: positive_words){
            		if(tweet.containsKey(pos)){
            			positive += tweet.get(pos);
            		}
            	}
            	for(String neg: negative_words){
            		if(tweet.containsKey(neg)){
            			negative += tweet.get(neg);
            		}
            	}
            	for(String ntrl: neutral_words){
            		if(tweet.containsKey(ntrl)){
            			neutral += tweet.get(ntrl);
            		}
            	}
        	if ( positive > negative && positive > neutral ){
        		return sentimentType.GOOD;
        	}
          	else if ( negative > positive && negative > neutral ){
                	return sentimentType.BAD;
            	}
            	else if ( neutral > positive && neutral > negative ){
                	return sentimentType.NEUTRAL;
            	}
	              
		return sentimentType.GOOD;
	}
	     
/*	public String registerUser(String userName, String passwd) {		
		PersistenceManager pm = PMF.get().getPersistenceManager();				
		Query query = pm.newQuery(UserInfo.class);
	    query.setFilter("userName == userNameParam");	    
	    query.declareParameters("String userNameParam");
	    try {
	        @SuppressWarnings("unchecked")
			List<UserInfo> results = (List<UserInfo>) query.execute(userName);
	        if (!results.isEmpty()) {
	        	pm.close();
	        	return null;
	        } else {
	        	UserInfo ui = new UserInfo(userName, passwd, "", "", "", "", "", "", "");											
	        	try {
	        		pm.makePersistent(ui);
	        	} finally {
	        		pm.close();
	        	}
	        }
        } finally {
            query.closeAll();
        }
	    return authorizationUrl;
	}*/	
}
	
class ValueComparator implements Comparator {

	  Map base;
	  public ValueComparator(Map base) {
	      this.base = base;
	  }

	  public int compare(Object a, Object b) {

	    if((Double)base.get(a) < (Double)base.get(b)) {
	      return 1;
	    } else if((Double)base.get(a) == (Double)base.get(b)) {
	      return 0;
	    } else {
	      return -1;
	    }
	  }
	}
